#ifndef FILTERPCAHOUGH_H
#define FILTERPCAHOUGH_H

// Classic libs
#include <iostream>
#include <vector>

#include <QObject>
#include <common/interfaces.h>

// Octrees
#include <vcg/space/index/octree.h>
#include <vcg/space/index/octree_template.h>
#include <wrap/utils.h>

// Points-Matrixes
#include <vcg/math/matrix33.h>
#include <vcg/space/deprecated_point3.h>
#include <vcg/space/box3.h>

// Circles and hough spaces
#include "../inc/Circle.h"
#include "../inc/HoughSpace4D.h"
#include "../inc/box4.h"

#include <list>

class QScriptEngine;

class filterPCAHough : public QObject, public MeshFilterInterface
{
	/* Pour pouvoir afficher le plugin dans le menu meshlab */
	Q_OBJECT
	Q_INTERFACES(MeshFilterInterface)

	public:
		enum { FP_PCA_HOUGH } ;
	
		/* Constructeur */
		filterPCAHough();
	
		virtual QString pluginName(void) const { return "HoughCirclePlugin"; }
		QString filterName(FilterIDType filter) const;
		QString filterInfo(FilterIDType filter) const;
		FilterClass getClass(QAction *a);
		void initParameterSet(QAction *,MeshModel &/*m*/, RichParameterSet & /*parent*/);
		QString filterScriptFunctionName(FilterIDType filterID);
                int postCondition( QAction* ) const {return MeshModel::MM_VERTCOORD | MeshModel::MM_VERTNORMAL | MeshModel::MM_VERTCURV ; }
		
		/* Appelle le filtre */
		bool applyFilter(QAction *filter, MeshDocument &md, RichParameterSet & par, vcg::CallBackPos * cb) ;
		
		/* ****************************************************************************************************** */
		/* ****************************************************************************************************** */
		// Beaucoup de parametres ne peuvent être passés en tant que const, même si ca aurait été mieux.
		/* ****************************************************************************************************** */
		/* ****************************************************************************************************** */
		
		
		/* Initialise le MeshModel de la classe */
		void init_meshModel ( MeshDocument &md, vcg::CallBackPos *cb );
		
		/* Plonge le maillage dans une grille carrée de résolution en X et en Y données */
                void init_grid1 ( MeshModel* _mesh, RichParameterSet &par, vcg::CallBackPos *cb, int grid_step = 1, const int& _resZ = 1);
		
		/* Recupere un tableau de pointeurs vers les points de la MNT */
        //void init_mntVert ( vcg::GridStaticPtr<CVertexO,float>& _grid, vcg::CallBackPos *cb );
        void init_mntVert(MeshDocument &md,RichParameterSet &par , vcg::CallBackPos *cb);
		
		/* Appelle toutes les initialisation necessaire au filtre */
		void init_filter ( MeshDocument &md, RichParameterSet &par, vcg::CallBackPos *cb );
		
		/* Mets dans le tableau _ptsMin, pour chaque case de la grille _grid, le point d'altitude minimum du nuage */
		void getPtsMin ( vcg::GridStaticPtr<CVertexO,float>& _grid, std::vector<CVertexO*>& _ptsMin );
		
		/* Supprime du maillage les points de la cellule passée en paramètre qui ne font pas partie de la coupe et rempli le tableau de sommets par ceux qui en font partie */
		void markNonSlicePoints ( vcg::GridStaticPtr<CVertexO,float>::Cell _start, vcg::GridStaticPtr<CVertexO,float>::Cell _end, const float _zMin, const float _lowThresh, const float _highThresh, std::vector<CVertexO*>& _slicePts );
		
		/* Sur un tableau de sommets, ne garde que les _n sommets de plus basse altitude */
        void markNPoints (std::vector<CVertexO*>& _pts );
		
		/* Marque les sommets du maillages comme étant supprimés (voir le flag delete) */
        bool markSlice( const float &_lowThresh, const float &_highTresh, vcg::CallBackPos *cb);
		
		
		/* ****************************************************************************************************** */
		/* ***************************************** PCA FUNCTIONS ********************************************** */
		/* ****************************************************************************************************** */
		
		bool pca_init_radius(RichParameterSet & par, vcg::CallBackPos *cb) ;

		bool pca_plane(RichParameterSet & par, vcg::CallBackPos *cb) ;
		
		float pca_init_moy( vcg::CallBackPos *cb ) ;
		
		/* ****************************************************************************************************** */
		/* ************************************* MAIN DIRECTION FUNCTIONS *************************************** */
		/* ****************************************************************************************************** */
		vcg::Point3f* getCellMeanDir ( MeshModel* _mm, vcg::GridStaticPtr<CVertexO,float> _grid, vcg::GridStaticPtr<CVertexO,float>::Cell* _cell );
		void getGridMeanDir ( MeshModel* _mm, vcg::GridStaticPtr<CVertexO,float> _grid, std::vector< vcg::Point3f* > &_rslt );
                vcg::Point3f* getCircleMainDir ( MeshModel* _mm, vcg::GridStaticPtr<CVertexO, float> _grid, RichParameterSet & par, Circle _circle) ;
                vcg::Point3f* getCircleMainDir2 ( MeshModel* _mm, vcg::GridStaticPtr<CVertexO, float> _grid, RichParameterSet & par, Circle _circle, vcg::Point4f &res) ;
                vcg::Point3f* getCircleMainDir3 (MeshModel* _mm, vcg::GridStaticPtr<CVertexO, float> _grid, RichParameterSet & par, Circle _circle,vcg::Point4f &res);
		
		/* ****************************************************************************************************** */
		/* ************************************* HOUGH SPACE FUNCTIONS ****************************************** */
		/* ****************************************************************************************************** */
                std::vector<Circle> hsCell ( vcg::GridStaticPtr<CVertexO,float> _grid, vcg::GridStaticPtr<CVertexO,float>::Cell* _cell, RichParameterSet &par );
                //std::vector<Circle> hsGrid( MeshModel* _mm, vcg::GridStaticPtr<CVertexO,float> _grid, RichParameterSet &par, vcg::CallBackPos *cb );

                /* _bb :
                 * - input : bounding box des données
                 * - output : bounding box d'une cellule de HS */
                std::vector<Circle> hsBb(int step, MeshModel* _mm, vcg::GridStaticPtr<CVertexO,float> _grid, vcg::Box4f & _bb, RichParameterSet &par, vcg::CallBackPos *cb, vcg::Point4f &res, MeshDocument *md = NULL );
		
                vector<vcg::Box4f> boxesFromCircleList( MeshModel* _mm, std::vector<Circle> circles, vcg::Point4f res ) ;

		/* ****************************************************************************************************** */
		/* ************************************** NEW LAYERS FUNCTIONS ****************************************** */
		/* ****************************************************************************************************** */
		MeshModel* createCirclesLayer ( MeshDocument& _parent, std::vector<Circle> _circles );
		MeshModel* createCirclesLayer ( MeshDocument& _parent, std::vector<Circle> _circles, float _height );
                void createCirclesLayer2 ( MeshDocument& _parent, std::vector<Circle> _circles, float _height, RichParameterSet &par, vcg::Point4f res );
                void createListCirclesLayer ( MeshModel *_mesh, std::vector<Circle> _circles, RichParameterSet &par, vcg::Point4f res  ) ;

                void createBbList ( MeshDocument& _parent, std::vector<vcg::Box4f> l) ;

		MeshModel* createMNTLayer ( MeshDocument& _parent, std::vector<CVertexO*> _mntVert );
		
		MeshModel* createSliceLayer ( MeshDocument& _parent );
		
                void addCircleToMesh(CMeshO &_mesh, Circle _c );
                void addCircleToMesh(CMeshO &_mesh, Circle _c, float _height );
		
		/* ****************************************************************************************************** */
		/* ****************************************** FLAGS FUNCTIONS ******************************************* */
		/* ****************************************************************************************************** */
		void flags_SelectAll ();
		void flags_ClearSelected();
		void flags_ClearVisited ();
		void flags_ClearDeleted ();
		
		static void getZMinMax ( vcg::GridStaticPtr<CVertexO,float>::Cell _start, vcg::GridStaticPtr<CVertexO,float>::Cell _end, float& _min, float& _max );
		
        void getMeshMin ( vcg::GridStaticPtr<CVertexO,float>& _grid);

        void getPtsFromMeshInBb(MeshModel* fullSlice, MeshModel* subSlice, vcg::Box3f box);

        /* ****************************************************************************************************** */
        /* ************************************** CIRCLES FILTER FUNCTIONS ************************************** */
        /* ****************************************************************************************************** */

        std::vector<Circle> applyCorrectionOnCircle(Circle &c, MeshModel* slice, float zmin, float zmax, float resR ,float alpha_r);
        std::vector<Circle> applyCorrectionOnCircle2(Circle &c, MeshModel* slice, float zmin, float zmax, float resR ,float alpha_r);
        std::vector<Circle> applyCorrectionOnCircle3(Circle &c, MeshModel* slice, float zmin, float zmax, float resR ,float alpha_r);
        float distPtToAxe(vcg::Point3f pt, Circle c);

	private :
		/* Input mesh */
		MeshModel* mesh;

        /* Minima vertices mesh */
        MeshModel* meshMinima;
	
		/* Variables for PCA */
		float radius ;
		
		/* Variables for dividing the mesh */
		vcg::GridStaticPtr<CVertexO,float> grid1;
		
		/* Vector containing pointers to the points being part of the mnt */
		std::vector<CVertexO*> mntVert;
		
		/* Un tableau qui contiendra les directions principales de chaque case de la grille */
                // std::vector < vcg::Point3f* > mainDirs;
};

#endif // FILTERPCAHOUGH_H
